<?php
/**
 * Created by PhpStorm.
 * User: godjarvis
 * Date: 2020/08/06
 * Time: 09:38
 */

namespace App\Model\Dao\DangKou;

use App\Model\Entity\DangKou\Product;
use App\Model\Entity\DangKou\ProductColor;
use App\Model\Entity\DangKou\ProductItem;
use App\Model\Entity\DangKou\TaosiFrontPropValueRules;
use App\Model\Entity\DangKou\TaosiFrontPropValues;
use App\Model\Entity\DangKou\TaosiProductProps;
use Fresh\Annotation\Autowired;
use Fresh\Annotation\ConnectionField;
use Fresh\Mvc\Model\Dao;
use Fresh\Weird\QueryBuilder;

class ProductDao extends Dao
{
    /**
     * @Autowired()
     * @var PromotionDao
     */
    public $promotionDao;

    /**
     * @ConnectionField(name="dangkou")
     */
    public function getProduct()
    {
        return $this->expr()
            ->field('p.(pid, pnid, image, number)')
            ->from(Product::class, 'p');
    }

    /**
     * @ConnectionField(name="dangkou")
     */
    public function getItemName($pnid)
    {
        return $this->expr()
            ->from(ProductItem::class)
            ->where('pnid=?', $pnid)
            ->query()
            ->pluck('name');
    }

    public function setProductConditions(QueryBuilder $qb, $params)
    {
        // 有图商品
        $qb->where('p.image!=""');

        // 在售 将售罄
        $qb->inWhere('p.is_sellable', [Product::SELL, Product::SELL_STOPPING]);

        // 货号或名称
        if ($params['keywords']) {
            $bindParams[] = "%" . $params['keywords'] . "%";
            $bindParams[] = "%" . $params['keywords'] . "%";
            $qb->join(ProductItem::class, 'p.pnid=pi.pnid', 'pi');
            $qb->where('pi.number LIKE ? or pi.`name` LIKE ?', $bindParams);
        }

        // 筛选属性
        if ($params['filter']) {
            $filterWhere = $this->getFilterWhere($params['filter']);
            $qb->where($filterWhere ?: 'false');
        }

        // 上新
        $qb->orderBy('p.date_shelved DESC');

        return $qb;
    }

    /**
     * 获取筛选的where条件
     * @ConnectionField(name="dangkou")
     */
    protected function getFilterWhere(array $filter)
    {
        // 该属性值的规则
        $rules = $this->createQueryBuilder()
            ->from(TaosiFrontPropValueRules::class)
            ->where('front_prop_id=?', $filter['fpid'])
            ->where('front_prop_value_id=?', $filter['fvid'])
            ->query()
            ->find();

        if (!$rules) {
            return '';
        }

        // 属性的多规则组合类型
        $rule_expression = $this->createQueryBuilder()
            ->from(TaosiFrontPropValues::class)
            ->where('id=?', $filter['fvid'])
            ->query()
            ->pluck('rule_expression');

        if (!$rule_expression) {
            return '';
        }

        // 规则表达式： $1 && $2 || $3
        $rule_expression = str_replace(['&&', '||'], [' AND ', ' OR '], $rule_expression);
        foreach ($rules as $rule) {
            $sql = $this->doRule($rule) ?: 'false';
            $rule_expression = str_replace('$' . $rule['id'], $sql, $rule_expression);
        }

        return $rule_expression;
    }

    /**
     * 根据规则获取pid
     * @ConnectionField(name="dangkou")
     */
    protected function doRule(array $rule)
    {
        $rule_where = '';
        switch ($rule['target']) {
            case TaosiFrontPropValueRules::TARGET_PROP:
                $rule_where = $this->getPropRule($rule);
                break;
            case TaosiFrontPropValueRules::TARGET_MERCHANT:
                $rule_where = $this->getMerchantRule($rule);
                break;
            case TaosiFrontPropValueRules::TARGET_BRAND:
                $rule_where = $this->getBrandRule($rule);
                break;
            case TaosiFrontPropValueRules::TARGET_CATEGORY:
                $rule_where = $this->getCategoryRule($rule);
                break;
            case TaosiFrontPropValueRules::TARGET_PRICE:
                $rule_where = $this->getPriceRule($rule);
                break;
            case TaosiFrontPropValueRules::TARGET_SECKILL_PRICE:
                $rule_where = $this->getSECKILLRule($rule);
                break;
            case TaosiFrontPropValueRules::TARGET_PROMOTION:
                $rule_where = $this->getPromotionRule($rule);
                break;
            case TaosiFrontPropValueRules::TARGET_SHELVE_DATE:
                $rule_where = $this->getShelveDateRule($rule);
                break;
        }

        return $rule_where;
    }


    public function getPropRule($rule)
    {
        $where = '';
        $propIdArr = explode(',', $rule['values']);
        switch ($rule['type']) {
            case TaosiFrontPropValueRules::TYPE_AND:
                foreach ($propIdArr as $propId) {
                    $where .= "(FIND_IN_SET({$propId}, prop_value_ids)) AND ";
                }
                $where = rtrim($where, 'AND ');
                break;
            case TaosiFrontPropValueRules::TYPE_OR:
                foreach ($propIdArr as $propId) {
                    $where .= "(FIND_IN_SET({$propId}, prop_value_ids)) OR ";
                }
                $where = rtrim($where, 'OR ');
                break;
            case TaosiFrontPropValueRules::TYPE_NOT:
                foreach ($propIdArr as $propId) {
                    $where .= "!(FIND_IN_SET({$propId}, prop_value_ids)) OR ";
                }
                $where = rtrim($where, 'OR ');
                break;
        }

        $pnids = $this->createQueryBuilder()
            ->from(TaosiProductProps::class)
            ->where('prop_id=?', $rule['relation_id'])
            ->where($where)
            ->field('pnid');

        $pids = $this->createQueryBuilder()
            ->from(Product::class)
            ->where('pnid in (%s)', $pnids)
            ->query()
            ->lists('pid');

        if ($pids) {
            return 'p.pid IN (' . implode(',', $pids) . ')';
        }

        return '';
    }

    public function getMerchantRule($rule)
    {
        $where = $this->getItemRule($rule, 'seller_id');

        if ($where) {
            $pnids = $this->getConditionPnids($where);

            if ($pnids) {
                return 'p.pnid IN (' . implode(',', $pnids) . ')';
            }
        }

        return '';
    }

    public function getBrandRule($rule)
    {
        $where = $this->getItemRule($rule, 'cgid');

        if ($where) {
            $pnids = $this->getConditionPnids($where);

            if ($pnids) {
                return 'p.pnid IN (' . implode(',', $pnids) . ')';
            }
        }

        return '';
    }

    public function getCategoryRule($rule)
    {
        $where = $this->getItemRule($rule, 'tid');

        if ($where) {
            $pnids = $this->getConditionPnids($where);

            if ($pnids) {
                return 'p.pnid IN (' . implode(',', $pnids) . ')';
            }
        }

        return '';
    }

    public function getPriceRule($rule)
    {
        list($min_price, $max_price) = explode(',', $rule['values']);
        $right_condition = $max_price ? ' && p.price_supply <= ' . $max_price . ')' : '';
        $left_bracket = $right_condition ? '(' : '';

        return $left_bracket . 'p.price_supply >= ' . $min_price . $right_condition;
    }

    public function getSECKILLRule($rule)
    {
        $pids = $this->promotionDao->getSecKillProductPids();
        if ($pids) {
            if (TaosiFrontPropValueRules::SECKILL_JOIN == $rule['values']) {
                return 'p.pid IN (' . implode(',', $pids) . ')';
            }

            if (TaosiFrontPropValueRules::SECKILL_OUT == $rule['values']) {
                return 'p.pid NOT IN (' . implode(',', $pids) . ')';
            }
        }

        return '';
    }

    public function getPromotionRule($rule)
    {
        $pids = $this->getPromotionProductPids($rule);

        if ($pids) {
            return 'p.pid IN (' . implode(',', $pids) . ')';
        }

        return '';
    }

    public function getShelveDateRule($rule)
    {
        if ($rule['values']) {
            [$timestamp_start, $timestamp_end] = explode(',', $rule['values']);
            return '(p.date_shelved >= ' . $timestamp_start . ' && p.date_shelved <= ' . $timestamp_end . ')';
        }

        return '';
    }

    public function getItemRule($rule, $item_column)
    {
        $where = '';
        $supplierIdArr = explode(',', $rule['values']);
        switch ($rule['type']) {
            case TaosiFrontPropValueRules::TYPE_AND:
                array_walk($supplierIdArr, static function (&$value) use ($item_column) {
                    $value = $item_column . ' = ' . $value;
                });
                $where = implode(' AND ', $supplierIdArr);
                break;
            case TaosiFrontPropValueRules::TYPE_OR:
                $where = $item_column . ' IN (' . implode(',', $supplierIdArr) . ')';
                break;
            case TaosiFrontPropValueRules::TYPE_NOT:
                array_walk($supplierIdArr, static function (&$value) use ($item_column) {
                    $value = $item_column . ' != ' . $value;
                });
                $where = implode(' OR ', $supplierIdArr);
                break;
        }

        return $where;
    }

    public function getConditionPnids($where)
    {
        return $this->expr()
            ->from(ProductItem::class)
            ->where($where)
            ->query()
            ->lists('pnid');
    }

    public function getPromotionProductPids($rule)
    {
        $promotionIds = explode(',', $rule['values']);
        return $this->promotionDao->getPromotionPids($promotionIds);
    }

    /**
     * @ConnectionField(name="dangkou")
     */
    public function getItemProducts($pnid)
    {
        return $this->expr()
            ->field('
            pi.(pnid,name,season,fabric,size_remark,description),
            p.(pid,image,price_mark,price_min,price_supply,color_id,number),
            pc.(color_name)
            ')
            ->from(ProductItem::class, 'pi')
            ->join(Product::class, 'pi.pnid=p.pnid', 'p')
            ->join(ProductColor::class, 'p.color_id=pc.color_id', 'pc')
            ->where('pi.pnid=?', $pnid)
            ->query()
            ->find();
    }

    /**
     * @ConnectionField(name="dangkou")
     */
    public function getProductInfo($pnid, $pid)
    {
        return $this->expr()
            ->field('p.image, pi.name, pi.price_supply price')
            ->from(Product::class, 'p')
            ->join(ProductItem::class, 'pi.pnid=p.pnid', 'pi')
            ->where('pi.pnid=?', $pnid)
            ->where('p.pid=?', $pid)
            ->query()
            ->findFirst();
    }
}
